home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 1 / Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso / FILES / DEV / A-B / 005.HierMenus.cpt / HierMenus.p < prev    next >
Text File  |  1988-08-01  |  11KB  |  331 lines

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    Hierarchical Menu Example Application
  6. #
  7. #    HierMenus
  8. #
  9. #    HierMenus.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.0                    8/88
  15. #
  16. #    Components:    HierMenus.p            August 1, 1988
  17. #                HierMenus.r            August 1, 1988
  18. #                HierMenus.make        August 1, 1988
  19. #
  20. #    This  program is a simple example of how to use hierarchical menus
  21. #    in your application.
  22. #
  23. #    See Sample and TESample for the general structure and MultiFinder
  24. #    techniques that we recommend that you use when building a new application.
  25. #
  26.  
  27. Hierarchical Menu Example
  28.  
  29. This program is a simple example of how to add hierarchical menus to your
  30. application. A few tips:
  31.  
  32. The toughest part is defining the menu item that "owns" the submenu. The
  33. item must be defined with a command-key of $1B, and a "mark" character
  34. whose value is the ID of the submenu. The submenu is defined just like
  35. a normal menu, but is inserted in the Menu Manager's private "hierarchical"
  36. menu list by specifying -1 as the second parameter to InsertMenu. 
  37.  
  38. It's easiest to declare all your menus (and submenus) as resources (as
  39. opposed to creating them at runtime with NewMenu), even if you're creating
  40. a submenu that will initially contain no items (like the Font submenu in 
  41. this example). This simplifies your program's handling of submenus (note
  42. that in this example, since the submenus' IDs follow the regular menu's
  43. ID, the only special handling of submenus is the test (in SetUpMenus) to
  44. decide whether to use 0 or -1 as the second parameter to InsertMenu!).
  45.  
  46. Submenus owned by applications' menus' IDs should always be in the range
  47. 1 through 235 (inclusive); desk accessories should use IDs 236 through
  48. 255, and should leave their menus and submenus in the menu bar only when
  49. they're active (that is, call InsertMenu when handling an activate event,
  50. and DeleteMenu when handling a deactivate event). This really applies to 
  51. all menus; it's most important for pop-up and submenus, however. All this
  52. to prevent conflicts (it may be helpful to note that in searching through 
  53. the menu list, the hierarchical list [containing all submenus & pop-up
  54. menus] is searched first).
  55.  
  56. This particular example is meant to show the mechanics of creating and
  57. handling events from submenus; in the interest of simplicity, certain nice
  58. features (like checkmarking the current font and styles, setting the 
  59. style of "real" font sizes in the Font submenu to "outline", etc) have been
  60. omitted.
  61.  
  62. Have fun.
  63.  
  64. Note: the current version does not actually call SysEnvirons to
  65. determine whether System 4.1 is running. This will be fixed, but
  66. in the meantime, run this with System 
  67. *************************************************************************}
  68.  
  69. PROGRAM HierMenus;
  70. {*
  71.  * Hierarchical Menu Example
  72.  * Bryan Stearns 05May87 
  73.  *
  74.  * (C) 1987 Apple Computer, Inc.
  75.  * All Rights Reserved.
  76.  *}
  77.  
  78. USES MemTypes, Quickdraw, OSIntf, ToolIntf, PackIntf;
  79.  
  80. {$R-} {no range checking}
  81. {$D+} {Generate debug symbols}
  82.  
  83. CONST
  84.     menuBase = 128;            {we number our menus starting at 128}
  85.     
  86.     mainMenu = menuBase;    {main menu ID}
  87.         {we don't care about the Font/Style/Size item numbers; we}
  88.         {deal with choices in the submenus directly}
  89.         quitItem = 5;            {Quit item number}
  90.     
  91.     {the Font submenu ID}
  92.     fontMenu = mainMenu+1;        
  93.         {we retrieve the font name from the menu by item number,}
  94.         {then use GetFNum(the font name) to convert to family}
  95.         {number. The font Name should be stored with the document,}
  96.         {not the family number (because Font/DA Mover renumbers)}
  97.         
  98.     {the Style submenu ID}
  99.     styleMenu = fontMenu+1;        
  100.         {Styles are dealt with in the same order they appear in}
  101.         {the Quickdraw "Style" type. Sneaky, but consistent with}
  102.         {other Macintosh applications}
  103.         
  104.     {the Size submenu ID}
  105.     sizeMenu = styleMenu+1;         {the Size submenu ID}
  106.         {Sizes are retrieved as text from the menu item; this would}
  107.         {allows advanced users to edit the menu to add custom sizes}
  108.         
  109.     lastMenu = sizeMenu;        {the last menu resource}
  110.     firstSubMenu = fontMenu;     {the first submenu - see SetUpMenus}
  111.     
  112.     myWINDid = 128;            {our window template}
  113.     myALRTid = 128;         {our “need right machine & sys software” alert}
  114.     mySTRListID = 128;        {our string list}
  115.     
  116. VAR    myWindow: WindowPtr;        {our window pointer}
  117.     myEvent: EventRecord;        {Event record for GetNextEvent}    
  118.     myMenus: ARRAY [menuBase..lastMenu] OF MenuHandle; {our menus}
  119.     done: BOOLEAN;                {a flag that the user chose Quit}
  120.     dragRect: Rect;                {drag limit rectangle, for DragWindow}
  121.     
  122.     demoStr1, demoStr2,            {Strings to draw in our demo window}
  123.     demoStr3, demoStr4: Str255;    
  124.     
  125.     curFont: INTEGER;            {the current font number}
  126.     curSize: INTEGER;
  127.     curStyle: Style;
  128.     
  129.     i: INTEGER;                    {a temp}
  130.     mResult: LongInt;            {temp, result from MenuSelect}
  131.     
  132.     
  133. {*
  134.  * Once-only initialization for my menus
  135.  * (Thanks, Andy!)
  136.  *}
  137. PROCEDURE SetUpMenus;
  138. VAR i: INTEGER;
  139. BEGIN
  140.     {Get and insert each menu. The normal menus are inserted using zero}
  141.     {as the second parameter to InsertMenu; the submenus get inserted using}
  142.     {-1, to tell the Menu Manager to insert in the hierarchical menu list}
  143.     FOR i := menuBase TO lastMenu DO BEGIN
  144.         myMenus[i] := GetMenu(i); {get the menu from the resource}
  145.         
  146.         {insert it into a menu bar, one way or another}
  147.         IF i < firstSubMenu THEN
  148.             {it's a regular menu-bar menu}
  149.             InsertMenu(myMenus[i],0) {insert at end of normal menu list}
  150.         ELSE 
  151.             {it's a submenu}
  152.             InsertMenu(myMenus[i],-1); {insert hierarchical submenu}
  153.     END; {for each menu}
  154.     
  155.     {Add the fonts to the Font submenu}
  156.     AddResMenu(myMenus[fontMenu],'FONT'); {add fonts to the font menu}
  157.     
  158.     DrawMenuBar; {draw our menus' titles}
  159. END; {setupmenus}
  160.  
  161.  
  162. {*
  163.  * Handle a command, either from a command key or from
  164.  * a menu choice. MResult is the value returned by 
  165.  * MenuSelect or MenuKey.
  166.  *}
  167. PROCEDURE DoCommand (mResult: LONGINT);
  168. VAR theItem,theMenu: INTEGER;
  169.     tmpStr: Str255;
  170.     tmpLong: LongInt;
  171. BEGIN
  172.     theItem := LoWord(mResult); {extract the menu and item numbers}
  173.     theMenu := HiWord(mResult);    
  174.     CASE theMenu OF {which menu?}
  175.         mainMenu:  CASE theItem OF
  176.             {Note that we don't have any cases for the font, style, or}
  177.             {size menus here: those are handled independently, below. Only}
  178.             {normal items that appear in this menu are taken care of here.}
  179.             quitItem: done := TRUE; 
  180.         END; {mainMenu case}
  181.  
  182.         fontMenu: BEGIN
  183.             {Here, we just change a global font number. However, if your}
  184.             {application saves font information in its documents, you should}
  185.             {save the font name (rather than the number), to avoid remapping}
  186.             {because of Font/DA Mover number changes. Each time you open}
  187.             {such a document, use GetFNum to find the right "current number".}
  188.             GetItem(myMenus[fontMenu],theItem,tmpStr); {get the chosen font’s name}
  189.             GetFNum(tmpStr,curFont); {get the new font’s number, to our global}
  190.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  191.         END; {fontMenu case}
  192.  
  193.         styleMenu: BEGIN
  194.             {The styles (other than Plain) are arranged in the}
  195.             {menu in the same order that they appear in the}
  196.             {Style type. If it's not plain, we can convert the}
  197.             {item number to the proper enumerated value for the style}
  198.             {and use set math to toggle the particular style}
  199.             {characteristic.}
  200.             IF theItem = 1 THEN curStyle := [] ELSE BEGIN {not 'Plain'}
  201.                 {it's not plain, so toggle this style characteristic}
  202.                 IF StyleItem(theItem-2) IN curStyle THEN
  203.                     {this style characteristic is currently on, so turn it off}
  204.                     curStyle := curStyle - [StyleItem(theItem-2)]
  205.                 ELSE
  206.                     {this style characteristic is currently on, so turn it off}
  207.                     curStyle := curStyle + [StyleItem(theItem-2)];
  208.             END;
  209.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  210.         END; {styleMenu case}
  211.  
  212.         sizeMenu: BEGIN
  213.             {Read the size out of the menu item}
  214.             GetItem(myMenus[sizeMenu],theItem,tmpStr); {get the chosen string}
  215.             StringToNum(tmpStr,tmpLong); {convert to longint}
  216.             curSize := tmpLong; {convert it to integer}
  217.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  218.         END; {sizeMenu case}
  219.         
  220.       END; {menu case}          
  221.     IF NOT done THEN HiliteMenu(0); {un-hilight the chosen menu item}
  222. END; {DoCommand}
  223.  
  224.  
  225. {*
  226.  * Main event loop
  227.  *}
  228. PROCEDURE MainEventLoop;
  229. VAR thePart, theHeight: INTEGER;
  230.     whichWindow: WindowPtr;
  231.     theChar: CHAR;
  232.     dragRect: Rect;
  233.     fInfo: FontInfo;
  234. BEGIN
  235.     done := FALSE; {we're not through yet!}
  236.     dragRect := screenBits.bounds;
  237.     
  238.     {Main event loop}
  239.     REPEAT  
  240.         SystemTask; {let the desk accs run}
  241.         IF GetNextEvent(everyEvent,myEvent) THEN WITH myEvent DO BEGIN
  242.             CASE what OF 
  243.                 mouseDown: BEGIN {"Click"}
  244.                     thePart := FindWindow(where,whichWindow);
  245.                     CASE thePart OF
  246.                         inSysWindow: SystemClick(myEvent,whichWindow);
  247.                         inMenuBar: DoCommand(MenuSelect(myEvent.where)); {nothing special here!}
  248.                         inDrag:    DragWindow(whichWindow,myEvent.where,dragRect);
  249.                     END; {case}
  250.                 END; {mouseDown}
  251.  
  252.                 keyDown, autoKey: BEGIN
  253.                     theChar := CHR(BitAnd(myEvent.message,charCodeMask));  {get the char}
  254.                     IF BitAnd(myEvent.modifiers,cmdKey) <> 0 THEN BEGIN
  255.                         DoCommand(MenuKey(theChar)); {pass it to the command handler}
  256.                     END ELSE BEGIN {not a command key}
  257.                         {ignore it}
  258.                     END; {if not command key}
  259.                 END; {keydown}
  260.  
  261.                 updateEvt: BEGIN {our window needs drawing}
  262.                     BeginUpdate(Windowptr(message));
  263.                     
  264.                     SetPort(myWindow); {make sure we're in our port}
  265.                     EraseRect(thePort^.portRect); {erase the old stuff}
  266.                     
  267.                     TextFont(curFont); {Set the right font/size/style}
  268.                     TextSize(curSize);
  269.                     TextFace(curStyle);
  270.                     
  271.                     GetFontInfo(fInfo);
  272.                     WITH fInfo DO theHeight := ascent+descent+leading;
  273.                     MoveTo(20,20+theHeight); DrawString(demoStr1);
  274.                     MoveTo(20,20+(theHeight*2)); DrawString(demoStr2);
  275.                     MoveTo(20,20+(theHeight*3)); DrawString(demoStr3);
  276.                     MoveTo(20,20+(theHeight*5)); DrawString(demoStr4);
  277.                                         
  278.                     EndUpdate(WindowPtr(message));
  279.                 END;
  280.             END; {case}
  281.         END; {if we got an event}
  282.         
  283.     UNTIL done; {keep it up until the user quits}
  284.     
  285. END; {maineventloop}
  286.  
  287.  
  288. {*
  289.  * Main
  290.  *}
  291. BEGIN
  292.     {Initialize all the usual managers}
  293.     InitGraf(@thePort);    
  294.     InitFonts;            
  295.     FlushEvents(everyEvent,0);
  296.     InitWindows;                
  297.     InitMenus;            
  298.     TEInit;
  299.     InitDialogs(NIL);            
  300.     InitCursor;            
  301.     
  302.     {Check to make sure we’re running on a machine}
  303.     {capable of supporting hierarchical menus; clearly,}
  304.     {we must do this before calling SetUpMenus!}
  305.     (** SysEnvirons(xxx); **)
  306.     IF FALSE THEN BEGIN {Sorry, see your dealer}
  307.         i := StopAlert(myALRTid,NIL); {put up the alert}
  308.         ExitToShell; {back to the finder}
  309.     END;
  310.     
  311.     curFont := 1; {Application font}
  312.     curSize := 0; {default size}
  313.     curStyle := []; {plain}
  314.     
  315.     SetUpMenus; {the oldest Macintosh subroutine!}
  316.     
  317.     {read the strings that we draw in our window}
  318.     {from a STR# resource}
  319.     GetIndString(demoStr1,mySTRListID,1);
  320.     GetIndString(demoStr2,mySTRListID,2);
  321.     GetIndString(demoStr3,mySTRListID,3);
  322.     GetIndString(demoStr4,mySTRListID,4);
  323.     
  324.     {Get our window}
  325.     myWindow := GetNewWindow(myWINDid,NIL,POINTER(-1));
  326.  
  327.     MainEventLoop; {handle events until the user quits}
  328. END.
  329.  
  330.  
  331.